﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;



namespace Kernelx.Extensions
{
    /// <summary>
    /// 
    /// </summary>
    public static class GenericExtensions
    {

        /// <summary> 
        /// 将source对象的属性的值拷贝到target对象的相同属性中
        /// </summary>
        /// <typeparam name="S"></typeparam>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="target"></param>
        /// <param name="isCopyField">是否拷贝字段的值(默认不拷贝)</param>
        /// <returns></returns>
        public static T CopyTo<S, T>(this S source, T target, bool isCopyField = false)
            where S : class
            where T : class
        {
            if (source != null && target != null)
            {
                var st = source.GetType();
                var tt = typeof(T) == typeof(object) ? target.GetType() : typeof(T);
                var props = tt.GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(x => x.CanRead);

                foreach (var sp in props)
                {
                    PropertyInfo tp;
                    if ((tp = st.GetProperty(sp.Name)) != null && sp.PropertyType == tp.PropertyType && tp.CanWrite)
                        tp.SetValue(target, sp.GetValue(source, sp.GetIndexValues()),
                            tp.GetIndexValues());
                }

                if (isCopyField)
                {
                    foreach (var sf in tt.GetFields())
                    {
                        var tf = st.GetField(sf.Name);
                        if (tf != null && sf.FieldType == tf.FieldType)
                            tf.SetValue(target, sf.GetValue(source));
                    }
                }

                return target;
            }
            throw new ArgumentException($"参数{nameof(source)}或{nameof(target)}均不允许为null");
        }



        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="S"></typeparam>
        /// <typeparam name="T"></typeparam>
        /// <param name="source"></param>
        /// <param name="target"></param>
        /// <param name="fetch"></param>
        /// <returns>return target</returns>
        public static T AutoSetvar<S, T>(this S source, T target, Func<S, string, object> fetch)
            where S : ICollection
            where T : class
        {
            if (source == null)
                throw new ArgumentNullException($"{nameof(source)}对象不能为null");
            if (target == null)
                throw new ArgumentNullException($"必须提供已实例化的对象{nameof(target)}");
            if (fetch == null)
                throw new ArgumentNullException($"必须提供参数{nameof(fetch)}");
            var props = typeof(T).GetProperties(BindingFlags.Instance | BindingFlags.Public);
            foreach (var p in props)
            {
                object v;
                if (p.CanWrite && (v = fetch(source, p.Name)) != null)
                {
                    if (p.PropertyType != v.GetType() && v.GetType() == typeof(string))
                        v = v.ConvertTo(p.PropertyType);

                    p.SetValue(target, v, p.GetIndexValues());
                }
            }
            return target;
        }



        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <param name="length"></param>
        /// <param name="getNextItem"></param>
        /// <returns></returns>
        public static IEnumerator<T> ToRange<T>(this T obj,int length,Func<T,T> getNextItem)
        {
            var current = obj;
            yield return current;
            while (length-- > 1)
                yield return current = getNextItem(current);

        }


    }
}
